﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using log4net;

namespace XmlModelPackager
{
    public class XmlGeneratorBase
    {
        public FileInfo OutputFile { get; private set; }
        private readonly XDocument _document = new XDocument();
        private readonly Stack<XElement> _elementStack = new Stack<XElement>();
        private readonly ILog _logger = LogManager.GetLogger(typeof (XmlGeneratorBase));

        public XmlGeneratorBase(FileInfo outputFile, string rootElementName)
        {
            OutputFile = outputFile;
            _elementStack.Push(new XElement(rootElementName));
            _document.Add(_elementStack.Peek());
        }

        public void WriteFile()
        {
            _logger.InfoFormat("Writing output to {0}", OutputFile.FullName);
            _document.Save(OutputFile.FullName);
        }

        protected void BeginElement(string name)
        {
            Debug.Assert(_elementStack.Count() < 1000, "Element stack @ 1,000 entries, missing EndElement() call!");

            var nextElement = new XElement(name);
            _elementStack.Peek().Add(nextElement);
            _elementStack.Push(nextElement);
        }

        protected void EndElement()
        {
            _elementStack.Pop();
        }

        protected void WriteAttribute(object value, string name)
        {
            _elementStack.Peek().Add(new XAttribute(name, value));
        }

        protected void WriteAttributes(object item, params string[] properties)
        {
            WriteAttributes(item,
                            properties.Select(property => new KeyValuePair<string, string>(property, property)).ToArray());
        }

        protected void WriteAttributes(object item, params KeyValuePair<string, string>[] properties)
        {
            var t = item.GetType();
            foreach (var property in properties)
            {
                var propInfo = t.GetProperty(property.Key);

                if (propInfo == null) throw new MissingMemberException(item.GetType().Name, property.Key);

                var value = propInfo.GetValue(item, null);

                if (value != null) _elementStack.Peek().Add(new XAttribute(property.Value, value));
            }
        }
    }
}